#ifndef __CELDA__
#define __CELDA__
#include <cassert>
#include <iostream>
#include <sstream>
#include <cstdlib>

using namespace std;
#define NOROESTE 1
#define NORESTE 2
#define SUDESTE 3
#define SUDOESTE 4

/** clase celda representa una celda solo tiene
 * se utiliza de la siguiente manera:
 * para crear una celda nula (osea q no solo
 * no es celda sino q no representa alguna
 * posicion en particular de la malla) se utiliza Celda()
 * para crear una celda vacia (osea una posicion) es Celda(int, int)
 * para crear una celda comun es Celda(0,0,x,y)
 * para crear una entrada/salida es Celda(orientacion, orientacion, x, y)
 * una Celda es representacion de una posicion de una malla solo si "esValida"
 * una Celda es una celda fisica de la malla solo si "esCelda", sino es un espacio en la malla
 * una Celda es entrada/salida si "esEntrada"/"esSalida"
 * una Celda tiene orientacionEntrada/orientacionSalida si "esEntrada"/"esSalida"
*/
class Celda {
    public:
        Celda(unsigned int orientacionEntrada, unsigned int orientacionSalida, unsigned int fila, unsigned int columna);
        Celda(unsigned int fila, unsigned int columna);
        Celda();
        Celda(const Celda&);
        /** dice si esta celda es en verdad una celda */
        bool esCelda() const;
        /** dice si es una celda de entrada*/
        bool esEntrada() const;
        /** dice si es una celda de salida*/
        bool esSalida() const;
        /** dice (en caso de ser entrada o salida) por que
         * nodo entra la corriente
         */
        unsigned int conexionEntrada() const;
        /** dice (en caso de ser entrada o salida) por que
         * nodo sale la corriente
         */
        unsigned int conexionSalida() const;
        /** dice si es una posicion valida en una malla*/
        bool esValida() const;
        /** operador de comparacion */
        bool operator==(const Celda& other);
        bool operator!=(const Celda& other);
        /** operador de asignacion */
        Celda& operator=(const Celda& other);
        unsigned int fila;
        unsigned int columna;

    private:
        /**
         * tanto entrada como salida tienen los mismos codigos:
         * 0 = NO es salida
         * 1 = conexion via nodo superior izquierda
         * 2 = conexion via nodo superior derecha
         * 3 = conexion via nodo inferior derecha
         * 4 = conexion via nodo inferior izquierda
         * (sentido horario)
         */
        int entrada;
        int salida;
        bool celda;

};

bool Celda::operator==(const Celda& other){
    if(this != &other){
        return this->celda == other.celda && this->fila == other.fila && this->columna == other.columna
                    && this->entrada == other.entrada && this->salida == other.salida;
    }
    return true;
}

bool Celda::operator!=(const Celda& other){
    return !(*this == other);
}

Celda& Celda::operator=(const Celda& other){
    if(this != &other){
        this->fila = other.fila;
        this->columna = other.columna;
        this->entrada = other.entrada;
        this->salida = other.salida;
        this->celda = other.celda;
    }
    return *this;
}

Celda::Celda() {
    this->fila = 0;
    this->columna = 0;
    this->entrada = -1;
    this->salida = -1;
    this->celda = false;
}

Celda::Celda(unsigned int fila, unsigned int columna){
    this->celda = false;
    this->fila = fila;
    this->columna = columna;
    this->entrada = 0;
    this->salida = 0;
}

Celda::Celda(const Celda& other){
    if(this != &other){
        this->fila = other.fila;
        this->columna = other.columna;
        this->entrada = other.entrada;
        this->salida = other.salida;
        this->celda = other.celda;
    }
}

/** recibe en caso de ser una celda entrada o de salida la orientacion , ademas recibe la posicion donde esta esta celda*/
Celda::Celda(unsigned int orientacionEntrada, unsigned int orientacionSalida, unsigned int fila, unsigned int columna){
    assert(orientacionEntrada >= 0 && orientacionEntrada < 5);
    assert(orientacionSalida >= 0 && orientacionSalida < 5);
    assert(orientacionEntrada == 0 || orientacionEntrada != orientacionSalida);
    this->celda = true;
    this->entrada = orientacionEntrada;
    this->salida = orientacionSalida;
    this->fila = fila;
    this->columna = columna;
	//cout << "CELD: " << this->celda <<" X: " << this->fila << " Y: " << this->columna << endl;
}

/** dice si esta celda es en verdad una celda */
bool Celda::esCelda() const{
    return this->celda;
}
/** dice si es una celda de entrada*/
bool Celda::esEntrada() const{
    return this->celda && this->entrada > 0;
}
/** dice si es una celda de salida*/
bool Celda::esSalida() const {
    return this->celda && this->salida > 0;
}
/** dice (en caso de ser entrada o salida) por que
    * nodo entra la corriente */
unsigned int Celda::conexionEntrada() const{
    assert(esEntrada()); //para asegurar...
    return this->entrada;
}

/** dice (en caso de ser entrada o salida) por que
    * nodo sale la corriente */
unsigned int Celda::conexionSalida() const{
    assert(esSalida()); //para asegurar
    return this->salida;
}

bool Celda::esValida() const{
    return this->entrada>=0 && this->salida>=0;
}

ostream& operator<<(ostream& out, const Celda& celda){
    stringstream s;
    s << "Celda:";
    if(celda.esValida()){
        s << " es valida";
        s << " - (x,y) = (" << celda.fila << "," << celda.columna << ")";
        if(celda.esCelda()){
            s<<" - es celda";
            if(celda.esEntrada()){
                s<< " - entrada:";
                if(celda.conexionEntrada() == NOROESTE)
                    s<<" NOROESTE";
                else if(celda.conexionEntrada() == NORESTE)
                    s<<" NORESTE";
                else if(celda.conexionEntrada() == SUDESTE)
                    s<<" SUDESTE";
                else if(celda.conexionEntrada() == SUDOESTE)
                    s<<" SUDOESTE";
            }
            if(celda.esSalida()){
                s<< " - tipo salida:";
                if(celda.conexionSalida() == NOROESTE)
                    s<<" NOROESTE";
                else if(celda.conexionSalida() == NORESTE)
                    s<<" NORESTE";
                else if(celda.conexionSalida() == SUDESTE)
                    s<<" SUDESTE";
                else if(celda.conexionSalida() == SUDOESTE)
                    s<<" SUDOESTE";
            }

        } else {
            s<<" - esta vacia";
        }
    } else {
        s<<" NULL";
    }
    out << s.str();
    return out;
}

#endif

